#include "laurin-discover-devices-module.h"
#include "laurin-utils.h"
#include "network/laurin-80211-access-point.h"
#include <glibmm/timeval.h>
#include <math.h>


/*
 * All time given in µswaiting.
 *
 * In a 802.11b frame we have: plcp preamble (144 or 72 bit) + plcp header (48 bit) + mpdu
 * plcp preamble can be shorter if IEEE80211_RADIOTAP_F_SHORTPRE is set.
 *
 * In a 802.11a frame we have: plcp preamble + plcp header + psdu + tail + pad
 *
 * plcp preamble: 12 symbol
 * plcp header: 24 bits of signal (coded in one ofdm symbol) + 16 bit of service field
 *
 * The pad is present because the number of bits present on the data are multiple of the bits coded for 1 ofdm symbol
 *
 * so we have : 12 symbol (preamble) + 1 symbol (signals) + variable symbols ( 16 bit (service field) + data bit + 6 bit (tail))
 *
 * the number of symbol in the variable part is
 * n_sym = (16 * bit_lenth_data + 6) / n_dbps 		where n_dbps is  = data_rate * OFDM_SYMBOL_TIME (4)
 *
 *
 */


#define PHY_PREAMBLE_CHAN_B  		144
#define PHY_PREAMBLE_CHAN_B_SHORT  72
#define PHY_HEADER_CHAN_B	  		48

#define PHY_TIME_CHAN_B				PHY_PREAMBLE_CHAN_B + PHY_HEADER_CHAN_B
#define PHY_TIME_CHAN_B_SHORT		PHY_PREAMBLE_CHAN_B_SHORT + PHY_HEADER_CHAN_B

#define OFDM_SYMBOL_TIME     		4
#define OFDM_SERVICE_FIELD_BITS		16
#define OFDM_TAIL_FIELD_BITS		6
#define OFDM_PLCP_BITS				OFDM_SERVICE_FIELD_BITS + OFDM_TAIL_FIELD_BITS
#define OFDM_PREAMBLE_TIME    		20

#define CHAN_A_SIFS_TIME			16
#define CHAN_B_SIFS_TIME			10






void processFrameExtForDevices(u_char* arg, const struct pcap_pkthdr *header,const u_char *data)
{
	((LaurinDiscoverDevicesModule*)arg)->processFrame(header, data);
}


LaurinDiscoverDevicesModule::LaurinDiscoverDevicesModule(const Glib::ustring& ifaceName, const Glib::ustring& masterIfaceName, LaurinModuleHandler* handler): LaurinModule(Glib::ustring("LAURIN DISCOVER DEVICES"), handler)
{
	_max_number_of_captured_frame = 1000;
	_capture_interval = 10;
	_is_location_enabled = false;

	_networks = NULL;

	// monitor device open automaticaly the capture context
	_device = new Laurin80211MonitorDevice(ifaceName, masterIfaceName);
	_errmess = _device->getErrorMessage();
	_handler = handler;
}


LaurinDiscoverDevicesModule::~LaurinDiscoverDevicesModule()
{
	_handler = NULL;

	// it delete all the used list and the _networks object
	clearResources();
	delete _device;

}


bool LaurinDiscoverDevicesModule::isInitialized() const
{
	return _device->isInitialized() && _handler != NULL;
}


void LaurinDiscoverDevicesModule::setMaxNumberOfCapturedFrame(int max)
{
	_max_number_of_captured_frame = max;
}


int LaurinDiscoverDevicesModule::getMaxNumberOfCapturedFrame() const
{
	return _max_number_of_captured_frame;
}


void LaurinDiscoverDevicesModule::setCaptureInterval(int interval)
{
	_capture_interval = interval;
}


int LaurinDiscoverDevicesModule::getCaptureInterval() const
{
	return _capture_interval;
}


bool LaurinDiscoverDevicesModule::isLocationEnabled() const
{
	return _is_location_enabled;
}


void LaurinDiscoverDevicesModule::setLocationEnable(bool flag)
{
	_is_location_enabled = flag;
}


Laurin80211NetworkSet* LaurinDiscoverDevicesModule::getNetworkSet()
{
	if(isWorking())
	{
		_errmess = "Modules is running. Can't get result while it's scanning.";
		return NULL;
	}

	return _networks;
}


bool LaurinDiscoverDevicesModule::run()
{
	clearResources();
	_networks = new Laurin80211NetworkSet();


	pcap_t* context = _device->getCaptureContext();
	bool ctrl = true;				// stop condition
	int counter = 0;				// captured frames

	Glib::TimeVal stop;
	Glib::TimeVal aux;
	Glib::TimeVal tmp;

	stop.assign_current_time();
	stop.add_seconds(_capture_interval);

	while(ctrl && counter < _max_number_of_captured_frame)
	{
		int r = pcap_dispatch(context, 1, processFrameExtForDevices, (u_char*) this);
		counter++;

		// r == -2 came from pcap_breakloop()
		tmp = stop;
		aux.assign_current_time();
		tmp -= aux;

		ctrl = !isStopRequestPending() && r != -2 && !tmp.negative();

	}


	if(_is_location_enabled)
	{
		doLocation();
		calculateDistanceFromAP();
	}

	return true;
}


void LaurinDiscoverDevicesModule::innerStop()
{
	pcap_breakloop(_device->getCaptureContext());
}


void LaurinDiscoverDevicesModule::processFrame(const struct pcap_pkthdr *header,const u_char *data)
{


	Laurin80211Frame frame;
	frame.setFrame(data, header->caplen);

	// parse the frame
	if(!frame.setFrame(data, header->caplen) || !frame.parse80211Frame())
		return;



	// frames are correctly filtered on parse80211Frame, so here we work only with correct frames.
	if(frame.FrameType == Laurin80211Frame::BEACON)
		addAccessPoint(&frame);

	else
	{
		// with location enabled we cannot retrieve clients on real time.
		if(_is_location_enabled)
		{
			if(frame.parseRadiotapHeader())
			{
				_collected_frames.push_front(frame);
			}

		}

		// without, we can!
		else
			addStation(&frame);
	}
}


void LaurinDiscoverDevicesModule::addStation(Laurin80211Station* station, bool checkAP)
{
	Laurin80211Network* n = _networks->isNetworkPresent(station->getBSSID());

	if(n)
	{
		// one station can be an AP. So we check if it's so.
		if(checkAP && n->isAccessPointPresent(station->getMAC()))
			return;

		n->addStation(*station, true);
	}
	// else the network is not (still?) present...we discard the frame...
}


void LaurinDiscoverDevicesModule::addStation(Laurin80211Frame* frame)
{
	//only DATA frames, ack haven't all the necessary fields! (i.e bssid!)
	if(frame->FrameType != Laurin80211Frame::DATA)
		return;

	Laurin80211Station s = Laurin80211Station(frame->getSourceAddress(), frame->getBSSIDAddress());
	addStation(&s, true);
}


void LaurinDiscoverDevicesModule::addAccessPoint(Laurin80211Frame* frame)
{

	if(frame->getBeaconDetails().IBSSFrame || (_is_location_enabled && !frame->parseRadiotapHeader()))
		return;


	Laurin80211Network* network;
	if((network = _networks->isNetworkPresent(frame->getBSSIDAddress())) != NULL)
	{
		// access point is alredy present

		if(_is_location_enabled)
		{
			Laurin80211AccessPoint* ap = network->isAccessPointPresent(frame->getBSSIDAddress());
			ap->addBeaconTSFT(frame->getRadioInformations().tstf);
		}
	}
	else
	{
		Laurin80211AccessPoint ap(frame->getBSSIDAddress(), frame->getBeaconDetails());

		if(_is_location_enabled)
			ap.addBeaconTSFT(frame->getRadioInformations().tstf);


		network = new Laurin80211Network(frame->getBeaconDetails().SSID);
		network->addAccessPoint(ap, false);
		_networks->addNetwork(*network, false);
		delete network;
	}
}


void LaurinDiscoverDevicesModule::doLocation()
{

	/*
	 * Here
	 *
	 * 1) We sort the frame in increasing tsft order. This because the pcap library dosn't guarantee that frames are captured on a strictly increasing order.
	 *
	 * 2) We search pairs of <data, ack> frames. For each pair founded we check who is the sender and who is the receiver checking the list of ap:
	 *    one of they is surely an ap because IBSS communication is not handled. After we can infer the flow direction.
	 *
	 * 3) If the communication is between an ap and an unknown station we add this last to the list of discovered stations, adding flow's informations.
	 *    If it's an already discovered station, we only add flow's informations.
	 *    The "locations" list serve for this purpose.
	 *
	 * 4) In point 2) if no correct ack is found, we check if the sender is an ap. If so, we don't do anything. Otherwise, we add the station to
	 *    a temporary list: "stations". Here are where lives the frames without flow's informations.
	 *
	 * 5) After have iterate over all the frames, we merge the stations and the locations list to found distinct station: these are the discovered stations.
	 *
	 */

	fprintf(stderr, "DEBUG: DO LOCATION N of PACKET: %d\n", _collected_frames.size());
	int usedpacket = 0;


	//sort the frames in increasing tstf values
	_collected_frames.sort();


	// iterators and list
	std::list<Laurin80211Frame>::iterator data = _collected_frames.begin();
	std::list<Laurin80211Frame>::iterator ack = _collected_frames.end();
	std::list<Laurin80211Frame>::iterator e = _collected_frames.end();

	std::list<Laurin80211Station*> locations;
	std::list<Laurin80211Station*> stations;


	while(data != e)
	{
		if(data->FrameType == Laurin80211Frame::DATA)
		{
			ack = data;
			ack++;

			// is the right ack frame?
			if(ack != e && ack->FrameType == Laurin80211Frame::ACK)
			{


				bool ackok = false;

				// some access point have two mac addresses. One is used for management's operations and the other for sending data's frames.
				// often an ack is sent for the management adapter so we need to check this possibility.
				u_int8_t* aux = generatePreviousMAC(ack->getDestinationAddress());

				if(getReadableMAC(ack->getDestinationAddress()) == getReadableMAC(data->getSourceAddress()) || getReadableMAC(aux) == getReadableMAC(data->getSourceAddress()))
					ackok = true;

				free(aux);
				aux = NULL;


				// is a correct ack?!?
				if(ackok)
				{

					// if we have a flow from station to ap, then direction is true, false otherwise
					bool direction = false;

					if(_networks->isNetworkPresent(ack->getDestinationAddress()) != NULL)
					{
						// the flow is from ap to station, aux will save the station mac address
						aux = data->getDestinationAddress();

					}
					else if(_networks->isNetworkPresent(data->getDestinationAddress()) != NULL)
					{
						// the flow is from station to ap
						aux = ack->getDestinationAddress();
						direction = true;

					}

					if(aux != NULL)
					{
						usedpacket++;

						Laurin80211Station* tmp = NULL;

						// check if station is an unknown one.
						// pay attention that only the station is checked, not the ap!
						std::list<Laurin80211Station*>::iterator lb = locations.begin();
						std::list<Laurin80211Station*>::iterator le = locations.end();

						while(lb != le && !tmp)
						{
							if(areMACEquals((*lb)->getMAC(), aux))
								tmp = *lb;

							lb++;
						}

						// new one? (not present on list?)
						if(!tmp)
						{
							// remember it, ssid will be setted after
							tmp = new Laurin80211Station(aux, data->getBSSIDAddress());
							locations.push_front(tmp);
						}

						Laurin80211Flow* flow = calculateDistance(*data, *ack, direction);
						if(flow != NULL)
							tmp->addFlow(*flow);

					}
				}

				// this is an ack frame, so we can skip this on the next iteration
				data++;
			}
			else
			{
				// add to a temp list
				stations.push_front(new Laurin80211Station(data->getSourceAddress(), data->getBSSIDAddress()));
			}
		}

		// goto next frame
		data++;
	}


	// now we need to merge the lists
	while(!stations.empty())
	{

		Laurin80211Station* tmp = stations.front();

		std::list<Laurin80211Station*>::iterator lb = locations.begin();
		std::list<Laurin80211Station*>::iterator le = locations.end();
		bool notfound = true;

		while(lb != le && notfound)
		{

			if(areMACEquals( tmp->getMAC(), (*lb)->getMAC()))
				notfound = false;

			lb++;
		}

		if(notfound)
			addStation(tmp, true);		// deep copy

		stations.pop_front();			// remove from list
		delete tmp;						// delete object
	}


	while(!locations.empty())
	{
		Laurin80211Station* tmp = locations.front();
		addStation(tmp, false);			// we don't need to check it's presence. We have alredy done it.
		locations.pop_front();
		delete tmp;
	}


	fprintf(stderr, "DEBUG: N OF USED PACKET %d\n", usedpacket);
}


int64_t LaurinDiscoverDevicesModule::calculateDurations(const Laurin80211Frame& frame)
{

	u_int8_t rate = frame.getRadioInformations().rate;
	bool short_preamble = frame.getRadioInformations().flags & IEEE80211_RADIOTAP_F_SHORTPRE;
	unsigned int length = frame.FrameLength;
	int numBits = length * 8;
	int64_t txTime;
	int bitsPerSymbol;
	int numSymbols;


	switch(frame.getRadioInformations().carrier)
	{
	case CARRIER_80211B:

		// rate are in terms of 500kbps. IEEE 802.11b avaibles data rates are: 1, 2, 5.5, 11.
		if(rate == 2 || rate == 4 || rate == 11 || rate == 22)
		{
			// we assume data rate 1 mbps for the PLCP preamble

			txTime = PHY_TIME_CHAN_B;
			if(short_preamble)
				txTime = PHY_TIME_CHAN_B_SHORT;

			txTime +=  (int) ceil (numBits / (rate / 2.0));
		}

		break;

	case CARRIER_80211A : case CARRIER_80211G:

		// avaibles data rates are: 6, 9, 12, 18, 24, 36, 48, 54
		if(rate == 12 || rate == 18 || rate == 24 || rate == 36 || rate == 48 || rate == 72 || rate == 96 ||  rate == 108 )
		{
			bitsPerSymbol = (rate / 2) * OFDM_SYMBOL_TIME;
			numBits	= numBits + OFDM_PLCP_BITS;
			numSymbols = (int) ceil(numBits / bitsPerSymbol);
			txTime = OFDM_PREAMBLE_TIME + (numSymbols * OFDM_SYMBOL_TIME);
		}

		break;

		/* TODO handle the case with 802.11g
		case CARRIER_80211G:

			// pbcc
			if(rate == 2 || rate == 4 || rate == 11 || rate == 22 || rate == 44 || rate == 66)
			{
				txTime = CCK_SIFS_TIME + phyTime + (int)ceil(numBits / (rate / 2.0));
				if(p->rate==66)
					txTime++;
			}
			// ofdm
			else if(rate == 12 || rate == 18 || rate == 24 || rate == 36 || rate == 48 || rate == 72 || rate == 96 || rate == 108)
			{
				bitsPerSymbol = (rate / 2) * OFDM_SYMBOL_TIME;
				numBits	= numBits + OFDM_PLCP_BITS;
				numSymbols	= (int)ceil (numBits / bitsPerSymbol);
				txTime		= OFDM_PREAMBLE_TIME + (numSymbols * OFDM_SYMBOL_TIME);

				phyTime	= CCK_PREAMBLE_BITS + CCK_PLCP_BITS;
						numBits		= framelen * 8;
						txTime		= CCK_SIFS_TIME + phyTime + DSSSOFTM_PREAMBLE_TIME
						+ (numSymbols * OFDM_SYMBOL_TIME);
						durations[count] = txTime;
						modes[count++] = MODE80211GL;

						phyTime /= 2;
						txTime		= CCK_SIFS_TIME + phyTime +DSSSOFTM_PREAMBLE_TIME
						+ (numSymbols * OFDM_SYMBOL_TIME);
						durations[count] = txTime;
						modes[count++] = MODE80211GS;
					}

		 */
	default:
		txTime =  -1;
	}

	return txTime;
}


Laurin80211Flow* LaurinDiscoverDevicesModule::calculateDistance(const Laurin80211Frame& data, const Laurin80211Frame& ack, bool direction)
{

	// if direction is true we have a frame flow from station (A) to access point (B)
	// from access point to station otherwise.

	// In the first case we have: 1) t_ack - t_data - tof_BM - sifs_time = tof_AB - tof_AM
	// on the second we have 2) t_ack - t_data - sifs_time - tof_BM = tof_AM + tof_AB;

	// we calculate based on the value of direction parameter 1) or 2). We don't consider here the value tof_BM. It will be setted after.

	Laurin80211Flow* flow = new Laurin80211Flow();

	flow->sifs_time = (data.getRadioInformations().carrier == CARRIER_80211A) ? CHAN_A_SIFS_TIME : CHAN_B_SIFS_TIME;
	flow->data_duration = calculateDurations(data);
	flow->data_tsft = data.getRadioInformations().tstf;
	flow->ack_tsft = ack.getRadioInformations().tstf;
	flow->direction = (direction) ?  Laurin80211Flow::FROM_AP_TO_STATION : Laurin80211Flow::FROM_STATION_TO_AP;

	// is not so important that ack have a > 0 duration.
	if(flow->data_duration < 0)
	{
		delete flow;
		return NULL;
	}

	return flow;
}


void LaurinDiscoverDevicesModule::calculateDistanceFromAP()
{

	std::list<Laurin80211Network>::iterator b = _networks->getNetworks()->begin();
	std::list<Laurin80211Network>::iterator e = _networks->getNetworks()->end();

	while(b != e)
	{
		std::list<Laurin80211AccessPoint>::iterator ab = b->getAccessPoints()->begin();
		std::list<Laurin80211AccessPoint>::iterator ae = b->getAccessPoints()->end();

		while(ab != ae)
		{
			if(ab->isLocationAvaible())
			{
				b->setDistanceBetweenMonitorAndAP(getReadableMAC(ab->getMAC()), ab->getDistanceFromMonitor());
			}

			ab++;
		}

		b++;
	}
}


void LaurinDiscoverDevicesModule::clearResources()
{
	_collected_frames.clear();


	//delete networks
	if(_networks)
		delete _networks;
}
